/*
 *   This File is Part of CuteOgre Game Engine
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *	Copyright 2010 Alexandre Pires ( c.alexandre.pires@gmail.com )
 *  Created on: 2010/08/22
 */

#include "Ogg/VorbisStream.h"
#include "Ogg/OggFile.h"

#include "assert.h"
#include "math.h"

VorbisStream::VorbisStream(int rSerial, OggFile* rFile) {
	mSerial = rSerial;
	mFile = rFile;
	mHeaderDone = false;
	mActive = true;
	ogg_stream_init(&mState, rSerial);
}

VorbisStream::~VorbisStream() {
	ogg_stream_clear(&mState);
	mFile = 0;
}

bool VorbisStream::handle_vorbis_header(ogg_packet* rPacket) {

	int ret = vorbis_synthesis_headerin(&mInfo, &mComment, rPacket);

	// Unlike libtheora, libvorbis does not provide a return value to
	// indicate that we've finished loading the headers and got the
	// first data packet. To detect this I check if I already know the
	// stream type and if the vorbis_synthesis_headerin call failed.
	if (mHeaderDone && ret == OV_ENOTVORBIS) {
		// First data packet
		return true;
	} else if (ret == 0) {
		mHeaderDone = true;
		mActive = true;
	}

	return false;
}

void VorbisStream::processHeaders() {

	ogg_packet packet;

	int ret = 0;
	bool done = false;

	vorbis_info_init(&mInfo);
	vorbis_comment_init(&mComment);

	while (!done && mFile->peekPacket(mSerial, &packet)) {

		done = handle_vorbis_header(&packet);

		if (!done) {
			// Consume the packet
			mFile->getPacket(mSerial, &packet);
		}
	}

	ret = vorbis_synthesis_init(&mDsp, &mInfo);
	assert ( ret == 0 );
	ret = vorbis_block_init(&mDsp, &mBlock);
	assert ( ret == 0 );
}

long VorbisStream::read(char *buffer, int length, int bigendianp, int word,
		int sgned) {

	int ret;

	if (!mHeaderDone || !mActive ) {
		return OV_EBADLINK;
	}

	float** pcm = 0;
	int samples = 0;

	int host_endian = hostIsBigEndian();
	int i, j;


	mMutex.lock();
	while (1) {
		samples = vorbis_synthesis_pcmout(&mDsp, &pcm);

		if (samples) {
			mMutex.unlock();
			break;
		}

		/* suck in another packet */
		{
			ogg_packet packet;

			mMutex.unlock();
			bool avail = mFile->getPacket(mSerial, &packet);
			mMutex.lock();

			if (!avail) {
				mMutex.unlock();
				return OV_EOF;
			}
			ogg_int64_t granulepos;
			granulepos = packet.granulepos;

			if (!vorbis_synthesis(&mBlock, &packet)) {

				/* lazy check for lazy header handling.  The
				 header packets aren't audio, so if/when we
				 submit them, vorbis_synthesis will
				 reject them */

				/* suck in the synthesis data and track bitrate */
				{

					int oldsamples = vorbis_synthesis_pcmout(&mDsp, NULL);

					/* for proper use of libvorbis within libvorbisfile,
					 oldsamples will always be zero. */

					if (oldsamples) {
						mMutex.unlock();
						return 0;
					}

					vorbis_synthesis_blockin(&mDsp, &mBlock);
				}
			}
		}
	}

	if (samples > 0) {

		mMutex.lock();
		long channels = mInfo.channels;
		long bytespersample = word * channels;

		if (samples > length / bytespersample)
			samples = length / bytespersample;

		if (samples <= 0)
			return OV_EINVAL;

		/* a tight loop to pack each size */
		{
			int val;

			if (word == 1) {
				int off = (sgned ? 0 : 128);
				for (j = 0; j < samples; j++)
					for (i = 0; i < channels; i++) {
						val = static_cast<int> (pcm[i][j] * 128.f);
						if (val > 127)
							val = 127;
						else if (val < -128)
							val = -128;
						*buffer++ = val + off;
					}
			} else {
				int off = (sgned ? 0 : 32768);

				if (host_endian == bigendianp) {
					if (sgned) {

						for (i = 0; i < channels; i++) { /* It's faster in this order */
							float *src = pcm[i];
							short *dest = ((short *) buffer) + i;
							for (j = 0; j < samples; j++) {
								val = static_cast<int> (src[j] * 32768.f);
								if (val > 32767)
									val = 32767;
								else if (val < -32768)
									val = -32768;
								*dest = val;
								dest += channels;
							}
						}

					} else {

						for (i = 0; i < channels; i++) {
							float *src = pcm[i];
							short *dest = ((short *) buffer) + i;
							for (j = 0; j < samples; j++) {
								val = static_cast<int> (src[j] * 32768.f);
								if (val > 32767)
									val = 32767;
								else if (val < -32768)
									val = -32768;
								*dest = val + off;
								dest += channels;
							}
						}

					}
				} else if (bigendianp) {

					for (j = 0; j < samples; j++)
						for (i = 0; i < channels; i++) {
							val = static_cast<int> (pcm[i][j] * 32768.f);
							if (val > 32767)
								val = 32767;
							else if (val < -32768)
								val = -32768;
							val += off;
							*buffer++ = (val >> 8);
							*buffer++ = (val & 0xff);
						}

				} else {
					int val;
					for (j = 0; j < samples; j++)
						for (i = 0; i < channels; i++) {
							val = static_cast<int> (pcm[i][j] * 32768.f);
							if (val > 32767)
								val = 32767;
							else if (val < -32768)
								val = -32768;
							val += off;
							*buffer++ = (val & 0xff);
							*buffer++ = (val >> 8);
						}
				}
			}
		}

		ret = vorbis_synthesis_read(&mDsp, samples);
		mMutex.unlock();
		return (samples * bytespersample);
	}

	return (samples);
}

bool VorbisStream::checkHeaderSignature(ogg_packet* rPacket) {

	char* sigblock = (char*) (rPacket->packet);
	unsigned char type = *sigblock++;

	QString signature = QString().fromAscii(sigblock, 6);

	if ((type == 0x01 || type == 0x03 || type == 0x05) && signature.contains(
			"vorbis")) {

		// VORBIS HEADER ( From vorbis format spec. )
		//Each header packet begins with the same header fields.
		//1    1) [packet_type] : 8 bit value (  the identification header is type 1, the comment header type 3 and the setup header type 5 )
		//2    2) 0x76, 0x6f, 0x72, 0x62, 0x69, 0x73: the characters ’v’,’o’,’r’,’b’,’i’,’s’ as six octets

		return true;
	}

	return false;
}

bool VorbisStream::checkHeaderSignature(ogg_page* rPage) {

	char* sigblock = (char*) (rPage->body);
	unsigned char type = *sigblock++;

	QString signature = QString().fromAscii(sigblock, 6);

	if ((type == 0x01 || type == 0x03 || type == 0x05) && signature.contains(
			"vorbis")) {

		// VORBIS HEADER ( From vorbis format spec. )
		//Each header packet begins with the same header fields.
		//1    1) [packet_type] : 8 bit value (  the identification header is type 1, the comment header type 3 and the setup header type 5 )
		//2    2) 0x76, 0x6f, 0x72, 0x62, 0x69, 0x73: the characters ’v’,’o’,’r’,’b’,’i’,’s’ as six octets

		return true;
	}

	return false;
}

int VorbisStream::hostIsBigEndian() {
	ogg_int32_t pattern = 0xfeedface; /* deadbeef */
	unsigned char *bytewise = (unsigned char *) &pattern;
	if (bytewise[0] == 0xfe)
		return 1;
	return 0;
}

